#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>

int tmp_sws_scale (AVFrame *frame, AVCodecContext *codec_ctx)
{
  struct SwsContext *sws_ctx;
  AVFrame *dst_frame = av_frame_alloc();
  int ret;
  uint8_t **src_data = NULL, **dst_data = NULL;
  int *src_linesize, *dst_linesize;
  int src_w = 320, src_h = 240, dst_w, dst_h;
  enum AVPixelFormat src_pix_fmt = AV_PIX_FMT_YUV420P,
                     dst_pix_fmt = AV_PIX_FMT_RGB24;

  src_w = codec_ctx->width;
  src_h = codec_ctx->height;
  src_pix_fmt = codec_ctx->pix_fmt;
  src_data = frame->data;
  src_linesize = frame->linesize;
  dst_w = codec_ctx->width;
  dst_h = codec_ctx->height;
  dst_data = dst_frame->data;
  dst_linesize = dst_frame->linesize;
  sws_ctx = sws_getContext(src_w, src_h, src_pix_fmt, dst_w, dst_h, dst_pix_fmt,
      SWS_BILINEAR, NULL, NULL, NULL);
  if (!sws_ctx) {
      fprintf(stderr, "Impossible to create scale context for the conversion "
          "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n",
          av_get_pix_fmt_name(src_pix_fmt), src_w, src_h,
          av_get_pix_fmt_name(dst_pix_fmt), dst_w, dst_h);
      ret = AVERROR(EINVAL);
      goto end;
  }
  /*
  if ((ret = av_image_alloc(src_data, src_linesize,
                            src_w, src_h, src_pix_fmt, 16)) < 0) {
      fprintf(stderr, "Could not allocate source image\n");
      goto end;
  }
  */
  if ((ret = av_image_alloc(dst_data, dst_linesize,
                            dst_w, dst_h, dst_pix_fmt, 1)) < 0) {
      fprintf(stderr, "Could not allocate destination image\n");
      goto end;
  }
  sws_scale(sws_ctx, (const uint8_t * const*)src_data,
      src_linesize, 0, src_h, dst_data, dst_linesize);
  for (int y = 0; y < codec_ctx->height; y++) {
      for (int x = 0; x < codec_ctx->width; x++) {
          uint8_t r = dst_frame->data[0][y * dst_frame->linesize[0] + x * 3];
          uint8_t g = dst_frame->data[0][y * dst_frame->linesize[0] + x * 3 + 1];
          uint8_t b = dst_frame->data[0][y * dst_frame->linesize[0] + x * 3 + 2];
          printf ("rgb:%d,%d,%d\t", r, g, b);
      }
  }
end:
  av_frame_free(&dst_frame);
  sws_freeContext(sws_ctx);
  return ret;
}

int read_frame (AVFormatContext *ifmt_ctx, int videoStreamIndex,
    AVCodecContext *dec_ctx)
{
  int ret = 0, stream_index = 0;
  AVPacket *packet = NULL;
  AVFrame *frame = av_frame_alloc();
  AVFrame *dec_frame = NULL;

  packet = av_packet_alloc();
  while (1) {
    if ((ret = av_read_frame(ifmt_ctx, packet)) < 0)
      break;
    stream_index = packet->stream_index;
    av_log (NULL, AV_LOG_DEBUG, "gave frame of stream #%u\n", stream_index);
    if (stream_index == videoStreamIndex) {
      ret = avcodec_send_packet(dec_ctx, packet);
      if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Decoding failed\n");
        break;
      }
      while (ret >= 0) {
        if (!(dec_frame = av_frame_alloc()))
          return AVERROR(ENOMEM);
        ret = avcodec_receive_frame(dec_ctx, dec_frame);
        if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
          break;
        else if (ret < 0)
          goto end;
        tmp_sws_scale (dec_frame, dec_ctx);
      }
      av_packet_unref(packet);
    }
  }

  av_frame_free(&frame);
  av_frame_free(&dec_frame);
end:
  return 0;
}
int main()
{
  avdevice_register_all();

  AVFormatContext *ifmt_ctx = NULL;
  int i = 0, ret = 0;

  avformat_open_input(&ifmt_ctx, "/dev/video0", NULL, NULL);
  avformat_find_stream_info(ifmt_ctx, NULL);

  int videoStreamIndex = -1;
  for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
    if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
      videoStreamIndex = i;
      break;
    }
  }
/*
  StreamContext *stream_ctx = NULL;
  stream_ctx = av_calloc(ifmt_ctx->nb_streams, sizeof (*stream_ctx));
  if (!stream_ctx)
    return AVERROR(ENOMEM);
    */
  for (i = 0; i < ifmt_ctx->nb_streams; i++) {
    AVStream *stream = ifmt_ctx->streams[i];
    const AVCodec *dec = avcodec_find_decoder(stream->codecpar->codec_id);
    AVCodecContext *codec_ctx;
    if (!dec) {
      av_log (NULL, AV_LOG_ERROR, "Failed to find decoder for stream #%u\n", i);
      return AVERROR_DECODER_NOT_FOUND;
    }
    codec_ctx = avcodec_alloc_context3(dec);
    if (!codec_ctx) {
      av_log (NULL, AV_LOG_ERROR,
          "Failed to allocate the decoder context for stream #%u\n", i);
      return AVERROR(ENOMEM);
    }
    ret = avcodec_parameters_to_context(codec_ctx, stream->codecpar);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Failed to copy decoder parameters to input decoder context "
               "for stream #%u\n", i);
        return ret;
    }
    /* Reencode video & audio and remux subtitles etc. */
    if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
            || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
      if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
          codec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, stream, NULL);
      /* Open decoder */
      ret = avcodec_open2(codec_ctx, dec, NULL);
      if (ret < 0) {
          av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i);
          return ret;
      }
      read_frame (ifmt_ctx, videoStreamIndex, codec_ctx);
    }
    avcodec_free_context(&codec_ctx);
  }
  avformat_close_input(&ifmt_ctx);
return 0;
}
